package com.jp.fm.servlet;

import java.io.IOException;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeMap;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.math.NumberUtils;

import com.jp.fm.HomeFinanceRunner;
import com.jp.fm.data.Budget;
import com.jp.fm.data.Category;

public class ShowTrends extends ServletParent {

	private static final int MAX_BUDGET_TIME_FRAME_MONTHS = 6;
	private static final int MAX_CATEGORIES_IN_PIE_CHART = 10;
	private static final int ACCOUNT_BALANCE_TIME_FRAME = 6;

	/**
	 *
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * Constructor of the object.
	 */
	public ShowTrends() {
		super();
	}

	/**
	 * Destruction of the servlet. <br>
	 */
	public final void destroy() {
		super.destroy(); // Just puts "destroy" string in log
		// Put your code here
	}

	public final void doGet(final HttpServletRequest request,
			final HttpServletResponse response) throws ServletException,
			IOException {

		doPost(request, response);
	}

	public final void doPost(final HttpServletRequest request,
			final HttpServletResponse response) throws ServletException,
			IOException {

		// Get data for the left nav bar
		getPageService().composeRequestLeftNav(request);

		composeTopCategoriesChartData(request);

		composeAccountBalancesChartData(request);

		composeBudgetChartData(request);

		RequestDispatcher rd = request
				.getRequestDispatcher("/secured/Trends.jsp");
		rd.forward(request, response);
	}

	protected void composeBudgetChartData(final HttpServletRequest request) {
		// Lookup budget items and add them to the response.
		String budgetTimeFrameStr = request.getParameter("budgetSelect");
		Calendar budgetCal = new GregorianCalendar();
		if (org.apache.commons.lang3.StringUtils.isEmpty(budgetTimeFrameStr)) {
			budgetTimeFrameStr = Integer
					.toString(budgetCal.get(Calendar.MONTH));
		}

		Integer budgetTimeFrame = Integer.parseInt(budgetTimeFrameStr);
		budgetCal.set(Calendar.MONTH, budgetTimeFrame);

		// Loop back 6 months and add each month
		Map<Date, Integer> budgetTimeFrameMap = new TreeMap<Date, Integer>();
		budgetCal.add(Calendar.MONTH, 1);
		for (int i = 0; i < MAX_BUDGET_TIME_FRAME_MONTHS; i++) {
			budgetCal.add(Calendar.MONTH, -1);
			budgetTimeFrameMap.put(budgetCal.getTime(),
					budgetCal.get(Calendar.MONTH));
		}
		request.setAttribute("budgetSelect", budgetTimeFrameStr);
		request.setAttribute("budgetTimeFrameMap", budgetTimeFrameMap);

		// Create start and end dates by which to lookup budget data
		// Determine if we need to roll back a year if the date range spans a
		// year cutoff
		int rollBackYear = 0;
		Calendar now = new GregorianCalendar();
		if (budgetTimeFrame > now.get(Calendar.MONTH)) {
			rollBackYear = -1;
		}

		Calendar startMonth = new GregorianCalendar();
		startMonth.set(Calendar.MONTH, budgetTimeFrame);
		startMonth.add(Calendar.YEAR, rollBackYear);
		startMonth.set(Calendar.DAY_OF_MONTH, 1);

		Calendar endMonth = new GregorianCalendar();
		endMonth.set(Calendar.MONTH, budgetTimeFrame);
		endMonth.add(Calendar.YEAR, rollBackYear);
		endMonth.set(Calendar.DAY_OF_MONTH,
				endMonth.getActualMaximum(Calendar.DAY_OF_MONTH));

		request.setAttribute("budgetSelectDate", startMonth.getTime());
		request.setAttribute("budgetEndDate", endMonth.getTime());

		List<String> alerts = new ArrayList<String>();
		List<Budget> budgetList = getBudgetService().findBudgetList(
				startMonth.getTime(), endMonth.getTime(), alerts);

		// Create Strings for budget chart
		StringBuffer budgetCategoryStr = new StringBuffer();
		StringBuffer budgetAmountStr = new StringBuffer();
		StringBuffer budgetCatAmountStr = new StringBuffer();
		int budgetListCounter = 0;
		DecimalFormat format = new DecimalFormat("0.00");
		for (Budget budget : budgetList) {
			budgetCategoryStr.append("'")
					.append(budget.getCategory().getName()).append("'");
			budgetAmountStr.append(format.format(budget.getAmount()));
			budgetCatAmountStr.append(format.format(budget.getCurrentAmount()));
			if (budgetListCounter < budgetList.size() - 1) {
				budgetCategoryStr.append(",");
				budgetAmountStr.append(",");
				budgetCatAmountStr.append(",");
			}
			budgetListCounter++;
		}

		request.setAttribute("budgetCategoryStr", budgetCategoryStr.toString());
		request.setAttribute("budgetAmountStr", budgetAmountStr.toString());
		request.setAttribute("budgetCatAmountStr",
				budgetCatAmountStr.toString());
	}

	protected void composeAccountBalancesChartData(
			final HttpServletRequest request) {
		// Get data for account balances over time
		getPageService().composeAccountBalancesOverTime(request,
				ACCOUNT_BALANCE_TIME_FRAME);
	}

	protected void composeTopCategoriesChartData(
			final HttpServletRequest request) {
		SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");

		// Add pie chart select options
		Map<String, String> timeFrameMap = new LinkedHashMap<String, String>();

		// Setup the pie chart select box
		Calendar now = new GregorianCalendar();
		// Find current date in month
		timeFrameMap
				.put("This month", Integer.toString(now.get(Calendar.DATE)));

		// Add entries for the last 5 months
		for (int i = 1; i <= 6; i++) {
			Calendar lastMonthCal = new GregorianCalendar();
			lastMonthCal.add(Calendar.MONTH, -i);
			lastMonthCal.set(Calendar.DATE,
					lastMonthCal.getActualMinimum(Calendar.DATE));
			String lastMonthStartDate = sdf.format(lastMonthCal.getTime());
			lastMonthCal.set(Calendar.DATE,
					lastMonthCal.getActualMaximum(Calendar.DATE));
			String lastMonthEndDate = sdf.format(lastMonthCal.getTime());
			timeFrameMap.put(
					lastMonthCal.getDisplayName(Calendar.MONTH, Calendar.LONG,
							Locale.getDefault())
							+ " "
							+ lastMonthCal.get(Calendar.YEAR),
					lastMonthStartDate + ":" + lastMonthEndDate);
		}

		timeFrameMap.put("Last six months", "180");
		timeFrameMap.put("Last year", "365");
		request.setAttribute("timeFrameMap", timeFrameMap);

		// Start Category Pie Chart
		// -----------------------------------------------------------------

		// Get the category chart parameter
		String timeFrameStr = request.getParameter("categoryPieSelect");

		if (null == timeFrameStr) {
			timeFrameStr = Integer.toString(now.get(Calendar.DATE));
		}

		// Find the chart title from the time frame map
		Set<String> keys = timeFrameMap.keySet();
		String value = null;
		for (String key : keys) {
			value = timeFrameMap.get(key);
			if (value.equals(timeFrameStr)) {
				request.setAttribute("categoryPieTitle", key);
				break;
			}
		}
		request.setAttribute("categoryPieSelect", timeFrameStr);

		Calendar startCal = new GregorianCalendar();
		Calendar endCal = new GregorianCalendar();

		if (NumberUtils.isNumber(timeFrameStr)) {
			Integer timeFrame = Integer.parseInt(timeFrameStr);
			startCal.add(Calendar.DATE, -timeFrame);
			endCal = now;
		} else {
			// must be a date range
			StringTokenizer tokenizer = new StringTokenizer(timeFrameStr, ":");
			String startDate = tokenizer.nextToken();
			String endDate = tokenizer.nextToken();

			try {
				startCal.setTime(sdf.parse(startDate));
				endCal.setTime(sdf.parse(endDate));
			} catch (ParseException e) {
				e.printStackTrace();
			}
		}

		// Get categories for pie chart
		Map<Float, Category> topCatMap = getCategoryService()
				.getTopCategoriesOverTimeFrame(startCal.getTime(),
						endCal.getTime());
		topCatMap = removeUnwantedCategories(topCatMap);
		// Add the start and end dates for the select time frame
		request.setAttribute("topCatsStartDate", sdf.format(startCal.getTime()));
		request.setAttribute("topCatsEndDate", sdf.format(endCal.getTime()));

		// loop through the top categories and combine the bottom cats
		// into one
		// Category called "Other"
		Map<Float, Category> topCatMapFinal = new TreeMap<Float, Category>(
				new HomeFinanceRunner.ReverseFloatComparator());

		Set<Entry<Float, Category>> entries = topCatMap.entrySet();
		int counter = 0;
		Float otherAmount = new Float(NumberUtils.FLOAT_ZERO);
		StringBuffer sb = new StringBuffer();
		for (Entry<Float, Category> entry : entries) {

			if (counter < MAX_CATEGORIES_IN_PIE_CHART) {
				topCatMapFinal.put(entry.getKey(), entry.getValue());
			} else {
				// add to Others amount
				otherAmount += entry.getKey();
				sb.append(entry.getValue().getName()).append(", ");
			}
			counter++;
		}
		sb.insert(0, "Others: ");
		sb.deleteCharAt(sb.length() - 2);
		Category otherCat = new Category(null, sb.toString());
		topCatMapFinal.put(otherAmount, otherCat);
		request.setAttribute("topCategories", topCatMapFinal);

		// End Category Pie Chart
		// -----------------------------------------------------------------
	}

	protected Map<Float, Category> removeUnwantedCategories(
			final Map<Float, Category> inMap) {

		Map<Float, Category> topCatMapFinal = new TreeMap<Float, Category>(
				new HomeFinanceRunner.ReverseFloatComparator());

		Set<Entry<Float, Category>> entries = inMap.entrySet();
		for (Entry<Float, Category> entry : entries) {

			if (!"Deposit".equals(entry.getValue().getName())) {

				topCatMapFinal.put(entry.getKey(), entry.getValue());
			}
		}

		return topCatMapFinal;
	}
}
